#include "mouse.h"

#include "graphics/graphics.h"
#include "application/game_application.h"

#ifdef _PLATFORM_WIN32
	#include "application/window_handler_win32.h"
#endif //#ifdef _PLATFORM_WIN32

#include "util/timer.h"				// log
#include "util/log.h"				// log
#include "util/common_macros.h"		// macros
#include "util/mmgr.h"		    

#include <cassert>
using namespace input;

Mouse& Mouse::Instance()
{
	static Mouse	Inst;
	return Inst;
}

Mouse::Mouse():
	m_HooverThreshold(0.2f),
	m_HooverTime(0.0f),
	m_MouseSpeed(0.002f),
	m_InitDragging(false),
	m_DragStarted(false),
    m_LastFingerID(BUTTON_LEFT)
{
	// setup the ID's
	for (int i=0; i<BUTTON_COUNT; i++)
	{
		m_ButtonA[i].ButtonID = (BUTTONS)i;
        m_FingerID[i] = 0;
        m_FingerPos[i] = math::Vec2(0,0);
	}

	m_DoubleClkTime = 0.3f;
	m_MinDragPos = 0.001f;
}

Mouse::~Mouse()
{
}


void		Mouse::OnMouseMove(const math::Vec2& P)
{
	// post move message
	size_t Count = m_Listeners.size();
	for (size_t i = 0; i<Count; i++)
	{
		if (m_Listeners[i] && m_Listeners[i]->IsEnabled())
			m_Listeners[i]->MouseMove(m_CurrentPos, P);
	}
	m_CurrentPos = P;
    
    m_FingerPos[m_LastFingerID] = P;
    
    // delete this
//#if defined( _PLATFORM_IOS) //|| defined(_PLATFORM_ANDROID)
//    m_ButtonA[BUTTON_LEFT].SetState( true, TIMER().Time());
//#endif
}


void		Mouse::OnMousePress(BUTTONS b)
{
	m_ButtonA[b].SetState( true, TIMER().Time());
	// notify all that key is pressed
	size_t Count = m_Listeners.size();
	for (size_t j = 0; j<Count; j++)
	{
		if (m_Listeners[j] && m_Listeners[j]->IsEnabled())
			m_Listeners[j]->MouseButtonPressed(&m_ButtonA[b], m_FingerPos[m_LastFingerID]);
	}	
    
    
}
void		Mouse::OnMouseRelease(BUTTONS b)
{
    
    
    //if(m_MouseDownCount < 2)
        m_ButtonA[b].SetState( false, TIMER().Time());
    
    m_FingerID[b] = 0;
    
	// notify all that key is pressed
	size_t Count = m_Listeners.size();
	for (size_t j = 0; j<Count; j++)
	{
		if (m_Listeners[j] && m_Listeners[j]->IsEnabled())
			m_Listeners[j]->MouseButtonReleased(&m_ButtonA[b], m_FingerPos[m_LastFingerID]);
	}	    
   

}
void		Mouse::OnMouseWheel(int Dir)
{
	if (Dir >= 1) Dir = 1; else if (Dir <=-1) Dir = -1;
	size_t Count = m_Listeners.size();
	for (size_t i = 0; i<Count; i++)
	{
		if (m_Listeners[i] && m_Listeners[i]->IsEnabled())
			m_Listeners[i]->MouseWheelMove(Dir, m_CurrentPos);
	}

}

void		Mouse::OnMouseEnter()
{
	size_t Count = m_Listeners.size();
	for (size_t j = 0; j<Count; j++)
	{
		if (m_Listeners[j] && m_Listeners[j]->IsEnabled())
			m_Listeners[j]->MouseEnter(m_CurrentPos);
	}
}

void		Mouse::OnMouseLeave()
{
	size_t Count = m_Listeners.size();
	for (size_t j = 0; j<Count; j++)
	{
		if (m_Listeners[j] && m_Listeners[j]->IsEnabled())
			m_Listeners[j]->MouseLeave(m_CurrentPos);
	}
}

bool Mouse::Tick(float dt)
{
//	if (g_pApp->IsPaused()) return true;	// do no logic
//	if (!g_pApp->MouseInside()) return true;

	const float		RepeatInterval = 0.1f;
	// number of listeners
	size_t Count = m_Listeners.size();

	float	Dist = (m_LastPos-m_CurrentPos).Magnitude();

	if (Dist < 0.001f)	// Didn't move..
	{	
		m_HooverTime+=dt;
		if (m_HooverTime>m_HooverThreshold)
		{
			// report to all listeners
			for (size_t i = 0; i<Count; i++)
			{
				if (m_Listeners[i] && m_Listeners[i]->IsEnabled())
					m_Listeners[i]->MouseHoover(m_CurrentPos);
			}	
		}
	} else
	{ // moved
		// stop hoover messages
		m_HooverTime = 0.0f;

		// are we in drag-n-drop mode ?
		if (m_DragStarted)
		{
			for (size_t i = 0; i<Count; i++)
			{
				if (m_Listeners[i] && m_Listeners[i]->IsEnabled())
					m_Listeners[i]->MouseOnDrag(m_StartDragPos, m_CurrentPos);
			}
		}
	}


	for (int i = 0; i<BUTTON_COUNT; i++)
	{
		m_ButtonA[i].ButtonChanged = false;

		// test for button down
		if (m_ButtonA[i].ButtonDown)
		{
			// notify all that key is pressed
			for (size_t j = 0; j<Count; j++)
			{
				if (m_Listeners[j] && m_Listeners[j]->IsEnabled())
					m_Listeners[j]->MouseButtonDown(&m_ButtonA[i], m_CurrentPos);
			}	
		}
	}

	// test for drag/drop operations
	if (m_ButtonA[BUTTON_LEFT].ButtonDown)
	{
		if (m_InitDragging && !m_DragStarted)
		{
			math::Vec2 DragDelta = m_CurrentPos - m_StartDragPos;
			m_DragStarted = DragDelta.MagSqr() >= (m_MinDragPos * m_MinDragPos);
		}
		else if (!m_InitDragging)
		{
			m_InitDragging = true;
			m_DragStarted = false;
			m_StartDragPos = m_CurrentPos;
		}
	} 
	if (m_InitDragging && !m_ButtonA[BUTTON_LEFT].ButtonDown)
	{
		if (m_DragStarted)
		{
			// notify all that button up
			for (size_t i = 0; i<Count; i++)
			{
				if (m_Listeners[i] && m_Listeners[i]->IsEnabled())
					m_Listeners[i]->MouseOnDrop(m_StartDragPos, m_CurrentPos);
			}
		}
		m_InitDragging = m_DragStarted = false;
	}
	m_LastPos = m_CurrentPos;
	return true;
}


/// Listener stuff... should really be abstracted into an AbstractInput
void Mouse::AddListener(MouseListener* _pListener)
{
	assert(_pListener && "Adding illegal listener!");
	// exists already
	int	Index = GetListenerIndex(_pListener);
	if (Index != -1)
		return;

	// PIERRE: NULL it, the listener calling check for NULL, AddListener look for a NULL slot first
	for (size_t i = 0; i < m_Listeners.size(); ++i) {
		if (!m_Listeners[i]) {
			// free slot use it
			m_Listeners[i] = _pListener;
			return;
		}
	}

	// no free slots add one
	m_Listeners.push_back(_pListener);
}

void Mouse::DropListener(MouseListener* _pListener)
{
	// exists already ?
	int	Index = GetListenerIndex(_pListener);
	if (Index == -1)
		return;

	// found it.. drop it

	// PIERRE: NULL it, the listener calling check for NULL, AddListener look for a NULL slot first
	m_Listeners[Index] = NULL;

	//m_Listeners.erase(m_Listeners.begin( ) + Index);
}

math::Vec2 Mouse::GetImmediatePos()
{
	// retrieve immediate state of mouse rather than whatever's updated normally

#ifdef _PLATFORM_WIN32

	// would using DInput get a more responsive result?
	// would the improvement be worth it over whatever issues DInput management/hardware/drivers may present?
	POINT pt;
	if (g_pApp->GetHandler()->HasFocus() &&
		::GetCursorPos(&pt) &&
		::ScreenToClient(::GetActiveWindow(), &pt))
	{
		math::Vec2 Pos = ((WindowHandlerWin32*)g_pApp->GetHandler())->MapPointToPos(pt.x, pt.y);
		UTIL_CLAMP(Pos.x, 0, 1);
		UTIL_CLAMP(Pos.y, 0, 1);
		return Pos;
	}
	else

		return GetPos();

#else
	return GetPos();
#endif
}

int	Mouse::GetListenerIndex(MouseListener* _pListener)
{
	size_t Size = m_Listeners.size();
	for (size_t i = 0; i<Size; i++)
	{
		if (m_Listeners[i] == _pListener)
			return (int)i;
	}	
	// not found
	return -1;
}

MouseListener::MouseListener() :
	m_IsEnabled(true)
{
	m_pMouse = &GET_MOUSE();
	if (m_pMouse )
		m_pMouse->AddListener(this);
	
}

MouseListener::~MouseListener()
{
	m_pMouse = &GET_MOUSE();
	if (m_pMouse )
		m_pMouse->DropListener(this);
}


Mouse::BUTTON_STATE::BUTTON_STATE() :
	ButtonID(BUTTON_LEFT),
	ButtonChanged(false),
	ButtonDown(false),
	RepeatInterval(0.0f)
{

}

void Mouse::BUTTON_STATE::SetState(bool Down, float TimeStamp)
{
	if (ButtonDown != Down)
	{
  		ButtonChanged  = true;
		ButtonDown	   = Down;		// is the key down ?
		ClickTime      = TimeStamp;		// when did it change last time ?
	}  else
	{
  		ButtonChanged  = false;
	}
}

Mouse::BUTTONS      Mouse::MapTouchToButton(long FingerID)
{
    for (int i=0; i<BUTTON_COUNT; i++)
	{
		if(m_FingerID[i] == FingerID)
        {
            m_LastFingerID = (Mouse::BUTTONS)i;
            return (Mouse::BUTTONS)i;
        }
    }
    
    for (int i=0; i<BUTTON_COUNT; i++)
	{
		if(m_FingerID[i] == 0)
        {
            m_LastFingerID = (Mouse::BUTTONS)i;
            m_FingerID[i] = FingerID;
            return (Mouse::BUTTONS)i;
        }
    }
    
       
    return BUTTON_CENTER;

}
